package cn.enjoy.redisson.util;


import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.lang.Nullable;

import java.util.concurrent.TimeUnit;

/**
 * @author K
 * @date 2021/7/1 下午5:26
 */
@SuppressWarnings("unchecked")
public class RedissonUtil {

    private final RedissonClient redissonClient;

//    private final List<RedissonClient> redissonClientList;

    /**
     * 默认超时时间，10秒
     */
    private static final long LEASE_TIME = 10;


//    public RedissonUtil(RedissonClient redissonClient, List<RedissonClient> redissonClientList) {
//        this.redissonClient = redissonClient;
//        this.redissonClientList = redissonClientList;
//    }

    public RedissonUtil(RedissonClient redissonClient) {
        this.redissonClient = redissonClient;
    }

    public RedissonClient getRedissonClient() {
        return redissonClient;
    }

//    public List<RedissonClient> getRedissonClientList() {
//        return redissonClientList;
//    }

    /**
     * 普通缓存放入
     *
     * @param key   key
     * @param value value
     */
    public void set(String key, String value) {
        redissonClient.getBucket(key).set(value);
    }

    /**
     * 普通缓存放入 并设置过期时间
     *
     * @param key    key
     * @param value  value
     * @param expire 过期时间 秒
     */
    public void set(String key, String value, long expire) {
        redissonClient.getBucket(key).set(value, expire, TimeUnit.SECONDS);
    }

    /**
     * 普通缓存放入 并设置过期时间
     *
     * @param key      key
     * @param value    value
     * @param expire   过期时间
     * @param timeUnit 时间单位
     */
    public void set(String key, String value, long expire, TimeUnit timeUnit) {
        redissonClient.getBucket(key).set(value, expire, timeUnit);
    }

    /**
     * 普通缓存获取
     *
     * @param key key
     */
    @Nullable
    public String get(String key) {
        Object value = redissonClient.getBucket(key).get();
        return value == null ? null : value.toString();
    }

    /**
     * 删除缓存
     *
     * @param key key
     * @return boolean
     */
    public boolean delete(String key) {
        return redissonClient.getBucket(key).delete();
    }

    /**
     * 删除缓存
     *
     * @param keys keys
     */
    public void delete(String... keys) {
        for (String key : keys) {
            redissonClient.getBucket(key).delete();
        }
    }

    /**
     * 获取key过期时间
     *
     * @param key key
     * @return 过期时间 秒
     */
    public Long getExpire(String key) {
        long expire = redissonClient.getBucket(key).remainTimeToLive();
        if (expire > 0) {
            expire = expire / 1000;
        }
        return expire;
    }

    /**
     * 设置缓存过期时间
     *
     * @param key      key
     * @param expire   expire
     * @param timeUnit 时间单位
     */
    public void expire(String key, int expire, TimeUnit timeUnit) {
        redissonClient.getBucket(key).expire(expire, timeUnit);
    }

    // ================================== 锁 =============================

    /**
     * 获取锁，阻塞
     * redisson提供看门狗，自动为锁续期30秒，每10秒检查续期（详细阅读源码）
     *
     * @param lockKey 锁名称
     * @return Lock
     */
    public RLock getLock(String lockKey) {
        return redissonClient.getLock(lockKey);
    }

    /**
     * 带过期时间的锁，阻塞
     * 锁定期间超出锁过期时间，不会为锁续期。超出锁过期时间后，锁定的key自动删除，unlock会抛异常
     *
     * @param lockKey 锁名称
     * @return RLock
     */
    public RLock lock(String lockKey) {
        return lock(lockKey, LEASE_TIME);
    }

    /**
     * 带过期时间的锁，阻塞
     * 锁定期间超出锁过期时间，不会为锁续期。超出锁过期时间后，锁定的key自动删除，unlock会抛异常
     *
     * @param lockKey 锁名称
     * @param timeout 时间单位 秒
     */
    public RLock lock(String lockKey, long timeout) {
        return lock(lockKey, timeout, TimeUnit.SECONDS);
    }

    /**
     * 带过期时间的锁，阻塞
     * 锁定期间超出锁过期时间，不会为锁续期。超出锁过期时间后，锁定的key自动删除，unlock会抛异常
     *
     * @param lockKey  锁名称
     * @param leaseTime  超时时间
     * @param timeUnit 时间单位
     */
    public RLock lock(String lockKey, long leaseTime, TimeUnit timeUnit) {
        RLock lock = redissonClient.getLock(lockKey);
        lock.lock(leaseTime, timeUnit);
        return lock;
    }

    /**
     * 尝试获取锁，拿不到锁就返回，默认有效期10秒
     *
     * @param lockKey 锁名称
     * @return boolean
     */
    public boolean tryLock(String lockKey) {
        return tryLock(lockKey, LEASE_TIME);
    }

    /**
     * 尝试获取锁，拿不到锁就返回，不需等待
     *
     * @param lockKey   锁名称
     * @param leaseTime 获取到锁后，锁持续时间。大于0不自动续期，小于0自动续期，单位：秒
     * @return boolean
     */
    public boolean tryLock(String lockKey, long leaseTime) {
        return tryLock(lockKey, 0, leaseTime);
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey   锁名称
     * @param waitTime  最多等待时间，单位：秒
     * @param leaseTime 获取到锁后，锁持续时间。大于0不自动续期，小于0自动续期，单位：秒
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime) {
        return tryLock(lockKey, waitTime, leaseTime, TimeUnit.SECONDS);
    }

    /**
     * 尝试获取锁
     *
     * @param lockKey   锁名称
     * @param waitTime  最多等待时间
     * @param leaseTime 获取到锁后，锁持续时间。大于0不自动续期，小于0自动续期
     * @param timeUnit  时间单位
     * @return boolean
     */
    public boolean tryLock(String lockKey, long waitTime, long leaseTime, TimeUnit timeUnit) {
        RLock lock = redissonClient.getLock(lockKey);
        try {
            return lock.tryLock(waitTime, leaseTime, timeUnit);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 公平锁（Fair Lock）
     * 在提供了自动过期解锁功能的同时，保证了当多个Redisson客户端线程同时请求加锁时，优先分配给先发出请求的线程。
     *
     * @param lockKey   锁名称
     * @param waitTime  最多等待时间
     * @param leaseTime 获取到锁后，锁持续时间。大于0不自动续期，小于0自动续期
     * @param timeUnit  时间单位
     * @return boolean
     */
    public boolean tryFairLock(String lockKey, int waitTime, int leaseTime, TimeUnit timeUnit) {
        RLock fairLock = redissonClient.getFairLock(lockKey);
        try {
            return fairLock.tryLock(waitTime, leaseTime, timeUnit);
        } catch (Exception e) {
            return false;
        }
    }

    /**
     * 释放锁
     *
     * @param lockKey 锁名称
     */
    public void unlock(String lockKey) {
        unlock(redissonClient.getLock(lockKey));
    }

    /**
     * 释放锁
     * 不能直接unlock，在解锁之前先判断要解锁的key是否已被锁定并且是否被当前线程保持。 如果满足条件时才解锁
     *
     * @param lock 锁对象
     */
    public void unlock(RLock lock) {
        if (lock != null && lock.isLocked()) {
            // 是否当前线程获取锁
            if (lock.isHeldByCurrentThread()) {
                lock.unlock();
            }
        }
    }

    /**
     * 红锁
     * 配置了3个独立节点
     */
//    public RedissonRedLock rLock(String key) {
//        RLock lock1 = redissonClientList.get(0).getLock(key);
//        RLock lock2 = redissonClientList.get(1).getLock(key);
//        RLock lock3 = redissonClientList.get(2).getLock(key);
//
//        return new RedissonRedLock(lock1, lock2, lock3);
//    }

}
